package com.etocrm.saas.lib.base.server.filter;

import cn.hutool.core.io.IoUtil;
import com.etocrm.saas.lib.base.log.requestlog.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * request增强filter
 * Created by Xs on 2022/12/14.
 */
@Slf4j
public class RequestWrapperFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (RequestUtil.isJsonRequest(servletRequest)) {
            filterChain.doFilter(new RequestWrapper((HttpServletRequest) servletRequest), servletResponse);
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }
}


@Slf4j
class RequestWrapper extends HttpServletRequestWrapper {

    private BufferedServletInputStream bufferedServletInputStream;

    public RequestWrapper(HttpServletRequest request) {
        super(request);

        try {
            bufferedServletInputStream = new BufferedServletInputStream(request.getInputStream());
        } catch (IOException e) {
            log.error("初始化RequestWrapper异常 : " + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public ServletInputStream getInputStream() {
        return bufferedServletInputStream;
    }
}


class BufferedServletInputStream extends ServletInputStream {
    private ServletInputStream servletInputStream;
    private ByteArrayInputStream cacheInputStream;

    public BufferedServletInputStream(ServletInputStream servletInputStream) throws IOException {
        this.servletInputStream = servletInputStream;
        this.cacheInputStream = new ByteArrayInputStream(IoUtil.readBytes(servletInputStream));
    }

    @Override
    public boolean isFinished() {
        return this.cacheInputStream.read() == -1;
    }

    @Override
    public boolean isReady() {
        return true;
    }

    @Override
    public void setReadListener(ReadListener listener) {
        this.servletInputStream.setReadListener(listener);
    }

    @Override
    public int read() {
        return cacheInputStream.read();
    }

    @Override
    public byte[] readAllBytes() {
        cacheInputStream.mark(0);
        byte[] bytes = cacheInputStream.readAllBytes();
        cacheInputStream.reset();
        return bytes;
    }

    @Override
    public int available() {
        return cacheInputStream.available();
    }

    @Override
    public void close() throws IOException {
        servletInputStream.close();
        cacheInputStream.close();
    }

    @Override
    public synchronized void mark(int readlimit) {
        cacheInputStream.mark(readlimit);
    }

    @Override
    public synchronized void reset() {
        cacheInputStream.reset();
    }

    @Override
    public boolean markSupported() {
        return cacheInputStream.markSupported();
    }

    @Override
    public long transferTo(OutputStream out) throws IOException {
        cacheInputStream.mark(0);
        long l = cacheInputStream.transferTo(out);
        cacheInputStream.reset();
        return l;
    }
}
